/**
 * Copyright 2010-2011 initialize4j.org
 * 
 * <pre>
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * </pre>
 * 
 * <pre>
 * http://www.apache.org/licenses/LICENSE-2.0
 * </pre>
 * 
 * <pre>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 * </pre>
 */
package org.initialize4j.modify;

import java.lang.reflect.Field;

import org.initialize4j.Initialize;
import org.initialize4j.Usage;
import org.initialize4j.provide.Provider;
import org.initialize4j.service.InitializationUtil;
import org.initialize4j.service.InitializeException;

import com.google.common.base.Preconditions;

/**
 * A builder to build {@link Command}s for initialization purpose.
 * 
 * @author <a href="hillger.t@gmail.com">hillger.t</a>
 */
public final class CommandBuilder {
	private Initialize initialize;
	private Field field;
	private Object bean;

	private CommandBuilder(Initialize initialize) {
		this.initialize = initialize;
	}

	public static CommandBuilder of(Initialize initialize) {
		return new CommandBuilder(initialize);
	}

	public CommandBuilder useBean(Object bean) {
		this.bean = bean;
		return this;
	}

	public CommandBuilder useField(Field field) {
		this.field = field;
		return this;
	}

	public Command build() {
		checkAllPreconditionsToBuildCommand();
		return this.initialize.use() == Usage.CONVERTER ? this.buildForConverter() : this.buildForProvider();
	}

	/**
	 * Makes all checks so that a command can successfully be build.
	 */
	private void checkAllPreconditionsToBuildCommand() {
		Preconditions.checkNotNull(this.initialize, "The initialize annotation should not be null.");
		Preconditions.checkNotNull(this.field, "The field has to be set.");
		Preconditions.checkNotNull(this.bean, "The bean has to be provided.");
	}

	/**
	 * Builds a {@link Command} which will be used in a {@link Modifier} related
	 * situation.
	 * 
	 * @return
	 */
	private Command buildForConverter() {
		return new Command() {

			@Override
			public void execute() throws InitializeException {
				Modifier modifier = new ConverterModifier(initialize.value());
				modifier.change(bean, field.getName());
			}
		};
	}

	/**
	 * Builds a {@link Command} which will be used in a {@link Provider} related
	 * situation.
	 * 
	 * @return
	 */
	private Command buildForProvider() {
		return new Command() {

			@Override
			public void execute() throws InitializeException {
				Provider<?> provider = InitializationUtil.instance(initialize.provider());
				Modifier modifier = new ProviderModifier(provider);
				modifier.change(bean, field.getName());
			}
		};
	}
}
